package com.wqj.kyee.gps.bluetooth;

import android.animation.LayoutTransition;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.CompoundButton;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;

import com.wqj.kyee.gps.R;
import com.wqj.kyee.gps.bluetooth.ble.BluetoothLeService;
import com.wqj.kyee.gps.service.NMEAParserService;
import com.wqj.kyee.gps.util.Constants;
import com.wqj.kyee.gps.util.ToastUtil;

import java.util.ArrayList;

public class BluetoothUI extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener,
                                                    View.OnClickListener, AdapterView.OnItemClickListener{

    private Switch mSwitchBT;
    private Switch mSwitchAutoConnect;

    private BluetoothAdapter mBluetoothAdapter;
    private boolean mScanning;
    private Handler mHandler;

    private RelativeLayout mBtDeviceLayout;
    private LayoutTransition mTransition;

    private FloatingActionButton mScanFab;
    private ProgressBar mScanPb;

    private ListView mBleDeviceListView;
    private LeDeviceListAdapter mLeDeviceListAdapter;

    private static final int REQUEST_ENABLE_BT = 1;
    // Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 10000;

    private final static String TAG = BluetoothUI.class.getSimpleName();

    private Toolbar mToolbar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bluetooth_ui);
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);

        //getSupportActionBar().setHomeButtonEnabled(true);
        //getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        mToolbar.setNavigationIcon(R.drawable.back);
        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });

        setStatus(R.string.title_not_connected);

        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
            finish();
        }

        mHandler = new Handler();

        mSwitchBT = (Switch) findViewById(R.id.id_switch_bt_on_off) ;
        mSwitchAutoConnect = (Switch) findViewById(R.id.id_switch_autoconnect);

        mBtDeviceLayout = (RelativeLayout) findViewById(R.id.id_rl_bt_device);

        mSwitchBT.setOnCheckedChangeListener(this);
        mSwitchAutoConnect.setOnCheckedChangeListener(this);

        initLayoutTransition();

        mBleDeviceListView = (ListView) findViewById(R.id.id_listview_ble_device);
        mBleDeviceListView.setOnItemClickListener(this);

        mScanFab = (FloatingActionButton) findViewById(R.id.id_fab_scan);
        mScanFab.setOnClickListener(this);

        mScanPb = (ProgressBar) findViewById(R.id.id_pb_scan);

        mScanning = false;

        initUI();


        final BluetoothManager bluetoothManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

        // Checks if Bluetooth is supported on the device.
        if (mBluetoothAdapter == null) {
            Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
            finish();
            return;
        }
    }

    private void initUI() {
        SharedPreferences sp = getSharedPreferences(Constants.BTDevice_Preferences, MODE_PRIVATE);
        boolean value = sp.getBoolean(Constants.SP_KEY_BT_IS_AUTO_CONNECT, false);
        if (value)
            mSwitchAutoConnect.setChecked(true);
    }

    @Override
    protected void onStart() {
        super.onStart();

        mLeDeviceListAdapter = new LeDeviceListAdapter();
        mBleDeviceListView.setAdapter(mLeDeviceListAdapter);

        registerReceiver(mBluetoothStateReceiver, makeBluetoothStateIntentFilter());
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Ensures Bluetooth is enabled on the device.  If Bluetooth is not currently enabled,
        // fire an intent to display a dialog asking the user to grant permission to enable it.

        if (!mBluetoothAdapter.isEnabled()) {
            //if (!mBluetoothAdapter.isEnabled()) {
                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
            //}
        } else {
            mSwitchBT.setChecked(true);
            if (!mScanning)
                scanLeDevice(true);
        }


        if (!isServiceBond) {
            final Intent intent = new Intent(this, NMEAParserService.class);
            isServiceBond = bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
        }

        registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
        //mScanFab.performClick();
    }

    @Override
    protected void onPause() {
        super.onPause();

        //mSwitchBT.setChecked(false);
        //mBtDeviceLayout.setVisibility(View.GONE);
        //scanLeDevice(false);
        //mScanFab.performClick();
        mLeDeviceListAdapter.clear();
        if (isServiceBond) {
            unbindService(mServiceConnection);
            isServiceBond = false;
        }
        unregisterReceiver(mGattUpdateReceiver);
    }

    @Override
    protected void onStop() {
        super.onStop();

        unregisterReceiver(mBluetoothStateReceiver);
    }


    private void initLayoutTransition() {
        mTransition = new LayoutTransition();
        //mTransition.setAnimator(LayoutTransition.APPEARING, mTransition.getAnimator(LayoutTransition.CHANGE_APPEARING));

        mBtDeviceLayout.setLayoutTransition(mTransition);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
            ToastUtil.show(this, getResources().getString(R.string.warn_open_bluetooth));
            finish();
            return;
        }

        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
        switch(compoundButton.getId()) {
            case R.id.id_switch_bt_on_off:
                if (isChecked) {
                    if (!mBluetoothAdapter.isEnabled()) {
                        if (!mBluetoothAdapter.isEnabled()) {
                            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
                        }
                    }
                    mBtDeviceLayout.setVisibility(View.VISIBLE);
                    //scanLeDevice(true);
                } else {
                    mBtDeviceLayout.setVisibility(View.GONE);
                    //scanLeDevice(false);
                    mBluetoothAdapter.disable();
                }
                break;
            case R.id.id_switch_autoconnect:
                SharedPreferences sp = getSharedPreferences(Constants.BTDevice_Preferences, MODE_PRIVATE);
                SharedPreferences.Editor editor = sp.edit();
                editor.putBoolean(Constants.SP_KEY_BT_IS_AUTO_CONNECT, isChecked);
                editor.commit();
                break;
            default:
                break;
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.id_fab_scan:
                if (mScanning) {
                    scanLeDevice(false);
                } else {
                    scanLeDevice(true);
                }
                break;
            default:
                break;
        }
    }

    private Boolean isServiceBond = false;
    private NMEAParserService mNMEAParserService;
    private final ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            Log.e(TAG, "onServiceConnected");
            mNMEAParserService = ((NMEAParserService.LocalBinder) service).getService();
            if ((mNMEAParserService!= null) && (mNMEAParserService.getConnectState())) {
                String deviceAddress = mNMEAParserService.getDeviceAddress();
                BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(deviceAddress);
                //String name = (device.getName() != null) ? device.getName() : device.getAddress();
                SharedPreferences sp = getSharedPreferences(Constants.BTDevice_Preferences, MODE_PRIVATE);
                String name = sp.getString(Constants.SP_KEY_BT_DEVICE_NAME, "");
                if (name.isEmpty()) {
                    name = (device.getName() != null) ? device.getName() : device.getAddress();
                }
                setStatus(getString(R.string.title_connected_to, name));
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.e(TAG, "onServiceDisconnected");
            mNMEAParserService = null;
        }
    };

    private BluetoothDevice mSelectedDevice;
    @Override
    public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
        final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
        if (device == null) return;

        String subTitle = mToolbar.getSubtitle().toString();
        if (subTitle.equals(getString(R.string.title_connecting))) {
            Log.e(TAG, "Connecting, do nothing");
            ToastUtil.show(this, R.string.info_connecting_wait);
            return ;
        }

        if (mScanning) {
            scanLeDevice(false);
        }

        mSelectedDevice = device;
//        final Intent intent = new Intent(this, BluetoothLeService.class);
//        intent.putExtra(BleSppActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress());
//        startService(intent);

        if (mNMEAParserService != null) {
            if (mNMEAParserService.getConnectState())
                mNMEAParserService.disconnect();
            mNMEAParserService.connect(mSelectedDevice.getAddress());
            setStatus(R.string.title_connecting);
        }
    }

    private void refreshUI() {
        if (mScanning) {
            mScanPb.setVisibility(View.VISIBLE);
            mScanFab.setImageResource(R.drawable.ic_action_cancel);
        } else {
            mScanPb.setVisibility(View.GONE);
            mScanFab.setImageResource(android.R.drawable.ic_menu_search);
        }
    }

    private void setStatus(final int resId) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mToolbar.setSubtitle(resId);
            }
        });
    }

    private void setStatus(final CharSequence subTitle) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mToolbar.setSubtitle(subTitle);
            }
        });
    }

    private static IntentFilter makeGattUpdateIntentFilter() {
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
        return intentFilter;
    }

    private static IntentFilter makeBluetoothStateIntentFilter() {
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
        return intentFilter;
    }

    private final BroadcastReceiver mBluetoothStateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
                switch (state) {
                    case BluetoothAdapter.STATE_TURNING_OFF:
                        Log.e(TAG, "Bluetooth turning off");
                        break;
                    case BluetoothAdapter.STATE_OFF:
                        Log.e(TAG, "Bluetooth  off");
                        if (mSwitchBT.isChecked())
                            mSwitchBT.setChecked(false);
                        mLeDeviceListAdapter.clear();
                        break;
                    case BluetoothAdapter.STATE_TURNING_ON:
                        Log.e(TAG, "Bluetooth turning on");
                        break;
                    case BluetoothAdapter.STATE_ON:
                        Log.e(TAG, "Bluetooth on");
                        if (!mSwitchBT.isChecked())
                            mSwitchBT.setChecked(true);
                        if (!mScanning)
                            scanLeDevice(true);
                        break;
                    default:
                        break;
                }
            }
        }
    };

    private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
                String name = (mSelectedDevice.getName() != null) ? mSelectedDevice.getName() : mSelectedDevice.getAddress();
                setStatus(getString(R.string.title_connected_to, name));

                SharedPreferences sp = getSharedPreferences(Constants.BTDevice_Preferences, MODE_PRIVATE);
                SharedPreferences.Editor editor = sp.edit();
                editor.putString(Constants.SP_KEY_BT_DEVICE_ADRESS, mSelectedDevice.getAddress());

                if (mSelectedDevice.getName() != null) {
                    editor.putString(Constants.SP_KEY_BT_DEVICE_NAME, mSelectedDevice.getName());
                }

                editor.commit();
            } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
                setStatus(R.string.title_not_connected);
            }
        }
    };

//    private void scanLeDevice(final boolean enable) {
//        if (enable) {
//            // Stops scanning after a pre-defined scan period.
//            mHandler.postDelayed(new Runnable() {
//                @Override
//                public void run() {
//                    mScanning = false;
//                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
//                    refreshUI();
//                }
//            }, SCAN_PERIOD);
//
//            mScanning = true;
//            mBluetoothAdapter.startLeScan(mLeScanCallback);
//        } else {
//            mScanning = false;
//            mBluetoothAdapter.stopLeScan(mLeScanCallback);
//        }
//        refreshUI();
//    }

    private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {

                @Override
                public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mLeDeviceListAdapter.addDevice(device);
                            mLeDeviceListAdapter.notifyDataSetChanged();
                        }
                    });
                }
            };

    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mLeDeviceListAdapter.clear();
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
                        if (scanner != null)
                            scanner.stopScan(leCallback);
                    }
                    else {
                        mBluetoothAdapter.stopLeScan(mLeScanCallback);
                    }
                    refreshUI();
                }
            }, SCAN_PERIOD);

            mScanning = true;

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
                if (scanner != null)
                    scanner.startScan(leCallback);
            }
            else{
                mBluetoothAdapter.startLeScan(mLeScanCallback);
            }
            //mBluetoothAdapter.startLeScan(mLeScanCallback);

        } else {
            mScanning = false;
            //       mBluetoothAdapter.stopLeScan(mLeScanCallback);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
                if (scanner != null)
                    scanner.stopScan(leCallback);
            }
            else{
                mBluetoothAdapter.stopLeScan(mLeScanCallback);
            }
        }

        refreshUI();
    }

    @SuppressLint("NewApi")
    private ScanCallback leCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                final BluetoothDevice device = result.getDevice();

                Log.e(TAG, "name " + device.getName() + "address = " + device.getAddress());

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mLeDeviceListAdapter.addDevice(device);
                        mLeDeviceListAdapter.notifyDataSetChanged();
                    }
                });
            }
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);

            ToastUtil.show(BluetoothUI.this, R.string.error_scan_ble_device);
        }
    };

    private class LeDeviceListAdapter extends BaseAdapter {
        private ArrayList<BluetoothDevice> mLeDevices;
        private LayoutInflater mInflator;

        public LeDeviceListAdapter() {
            super();
            mLeDevices = new ArrayList<BluetoothDevice>();
            mInflator = BluetoothUI.this.getLayoutInflater();
        }

        public void addDevice(BluetoothDevice device) {
            if(!mLeDevices.contains(device)) {
                mLeDevices.add(device);
            }
        }

        public BluetoothDevice getDevice(int position) {
            return mLeDevices.get(position);
        }

        public void clear() {
            mLeDevices.clear();
        }

        @Override
        public int getCount() {
            return mLeDevices.size();
        }

        @Override
        public Object getItem(int i) {
            return mLeDevices.get(i);
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public View getView(int i, View view, ViewGroup viewGroup) {
            ViewHolder viewHolder;
            // General ListView optimization code.
            if (view == null) {
                view = mInflator.inflate(R.layout.ble_devie_list_item, null);
                viewHolder = new ViewHolder();
                viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
                viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
                view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) view.getTag();
            }

            BluetoothDevice device = mLeDevices.get(i);
            final String deviceName = device.getName();
            if (deviceName != null && deviceName.length() > 0)
                viewHolder.deviceName.setText(deviceName);
            else
                viewHolder.deviceName.setText(R.string.title_unknown_ble_device_name);
            viewHolder.deviceAddress.setText(device.getAddress());

            return view;
        }
    }

    static class ViewHolder {
        TextView deviceName;
        TextView deviceAddress;
    }
}
